package cn.yjh.support;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

import javax.annotation.Resource;

import cn.yjh.annotation.RPC;
import cn.yjh.exception.ServicesException;
import cn.yjh.utils.AnnotationElementUtils;
import cn.yjh.utils.Assert;
import cn.yjh.utils.ClassUtils;
import cn.yjh.utils.StringUtils;

/**
 * @DESC:
 * @AUTHOR: YouJinhua
 * @DATE: 2022-01-09 12:38:14
 * @VERSION: 1.0
 */
public class DefaultServiceFactory implements ServiceFactory,ServiceRegister {

    protected String scanPackage = null;

    // 一级缓存，存放的是已经实例化，并且初始化后的单例对象（也叫单例池）
    protected final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 三级缓存，存放的是 ObjectFactory，通过 ObjectFactory 的 getObject()可以拿到已经实例化，但是并未初始化的对象
    protected final Map<String, Supplier<?>> singletonFactories = new HashMap<>(16);

    // 二级缓存，存放的是已经实例化，但是并未初始化的对象
    protected final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    public DefaultServiceFactory(String scanPackage) {
        this.scanPackage = scanPackage;
        registerServiceDefinition();
    }

    @Override
    public void registerServiceDefinition() {
        Set<Class> clazzes = ClassUtils.getClassesByPackageName(scanPackage);
        Iterator<Class> iterator = clazzes.iterator();
        while (iterator.hasNext()){
            Class clazz = iterator.next();
            if(AnnotationElementUtils.isAnnotationPresent(clazz,RPC.class)){
                registerDefinition(clazz);
            }
        }
    }

    @Override
    public Object getService(String serviceName) throws ServicesException {
        Assert.hasLength(serviceName,"serviceName can not be null!");
        ServiceDefinition definition = getDefinition(serviceName);
        return doCreateService(definition);
    }

    @Override
    public <T> T getService(String name, Class<T> requiredType) throws ServicesException {
        Assert.notNull(requiredType);
        Assert.notNull(name);
        return (T) this.getService(name);
    }

    @Override
    public <T> T getService(Class<T> requiredType) throws ServicesException {
        T t = null;
        Assert.notNull(requiredType);
        Set<ServiceDefinition> definitions = getDefinitions(requiredType);
        if(definitions.size()>1){
            throw new ServicesException("the type: ["+requiredType.getName()+"] not only one");
        }else{
            Iterator<ServiceDefinition> iterator = definitions.iterator();
            if(iterator.hasNext()){
                ServiceDefinition next = iterator.next();
                String defaultServiceName = next.defaultServiceName;
                t = getService(defaultServiceName,requiredType);
            }
        }
        return t;
    }

    @Override
    public boolean containsService(String name) {
        return containsName(name);
    }

    private Object doCreateService(ServiceDefinition definition){
        Class serviceClazz = definition.serviceClazz;
        Object o = null;
        try {
            o = serviceClazz.newInstance();
            // 添加到缓存中
            this.earlySingletonObjects.put(definition.defaultServiceName,o);

            initService(definition,o);

        } catch (InstantiationException e ) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ServicesException e) {
            e.printStackTrace();
        }
        return o;
    }

    private void initService(ServiceDefinition definition, Object o) throws ServicesException{
        Class serviceClazz = definition.serviceClazz;
        Field[] fields = serviceClazz.getDeclaredFields();
        for (Field field : fields) {
            if(field.isAnnotationPresent(Resource.class)){
                Resource resource = field.getAnnotation(Resource.class);
                String defaultServiceName = resource.name();
                ServiceDefinition def = null;
                if(StringUtils.isNotEmpty(defaultServiceName)){
                    def = getDefinition(defaultServiceName);
                }else{
                    Class<?> type = field.getType();
                    Set<ServiceDefinition> definitions = getDefinitions(type);
                    if(definitions.size()>1){
                        throw new ServicesException("the type: ["+type.getName()+"] not only one");
                    }else{
                        Iterator<ServiceDefinition> iterator = definitions.iterator();
                        if(iterator.hasNext()){
                            def = iterator.next();
                            defaultServiceName = def.defaultServiceName;
                        }
                    }
                }
                Object singleton = getSingleton(defaultServiceName);
                if(singleton == null){
                    singleton = doCreateService(def);
                }
                field.setAccessible(true);
                try {
                    field.set(o,singleton);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }

        }
    }

    private Object getSingleton(String serviceName) {
        Object singletonObject = this.singletonObjects.get(serviceName);
        if (singletonObject == null) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(serviceName);
                if (singletonObject == null) {
                    Supplier<?> singletonFactory = this.singletonFactories.get(serviceName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.get();
                        this.earlySingletonObjects.put(serviceName, singletonObject);
                        this.singletonFactories.remove(serviceName);
                    }
                }
            }
        }
        return singletonObject;
    }
}
